/* -*- c++ -*-
   parser.h - LLVMIR Parsers
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

#ifndef PARSER_H
#define PARSER_H

#include "llvmir.h"

#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/GlobalValue.h"
#include "llvm/InlineAsm.h"
#include "llvm/Function.h"
#include "llvm/BasicBlock.h"
#include "llvm/Instruction.h"
#include "llvm/Instructions.h"
#include "llvm/Constants.h"

/* 
 * Parsers. A parser object handles all front-end actions, including
 * reading source code from a file, compiling to to LLVM and the
 * translation to GENERIC trees.
 */

namespace llvmir
{
  /*
   * Interface for LLVMIR parsers and generic functions.
   */
  class Parser
  {
  public:
    /* Constructor */
    explicit Parser()
    { /* skip */ }

    /* Destructor */
    virtual ~Parser()
    { /* skip */ }

    /*
     * Methods (in convert.cc unless otherwise stated).
     */

    /* Test whether the parser accepts input from files with a given
       name.  Used to determine whether the parser should be selected
       for an input file.  May be called before the parser is
       initialised.
    */
    virtual bool 
    accepts_filename(Context* context ATTRIBUTE_UNUSED, 
                     const std::string& filename ATTRIBUTE_UNUSED)
    {
      return false;
    }

    /* Initialise the parser */
    virtual void
    init(Context* context ATTRIBUTE_UNUSED)
    { /* skip */ }

    /* Process options, called after all command line arguments have
       been processed. May be called repeatedly with the same
       options. */
    virtual bool
    process_options(Options* options ATTRIBUTE_UNUSED)
    {
      return true;
    }

    /* Read an LLVM module from a file. */
    virtual llvm::Module* 
    parse_file(Context* context ATTRIBUTE_UNUSED,
               const char* filename ATTRIBUTE_UNUSED)
    {
      return 0;
    }

    /* Compile an LLVM module, return true on success. */
    virtual bool
    compile_module(Context *context, const llvm::Module *llvm_module);

    /* Read and compile an LLVM file, return true on success. */
    virtual bool
    compile_file(Context *context, const char* filename);

    /*
     * Conversion functions.
     */

    /*
     * Meta-Data 
     */

    /* Convert a meta-data object. This is the top-level for all
       meta-data objects that can appear as a value. By default, this
       always returns an undefined value. */
    virtual ValueRepr*
    convert_metadata(Context *context,
                     const llvm::Value *llvm_value);

    /* Process a module-level meta-data object. */
    virtual void 
    process_module_metadata(Context *context,
                            const llvm::NamedMDNode* llvm_metadata);

    /* Process a module-level assembler string. */
    virtual void 
    process_module_asm(Context *context,
                       const std::string& llvm_asm_str);

    /*
     * Types (in convert_types.cc)
     */

    /* Convert an LLVM type.  This is the top-level function for
       converting types. It adds newly created representation objects to
       the tables in context->Scope.
    */
    virtual TypeRepr* 
    convert_type(Context *context, const llvm::Type *llvm_type);

    /* Convert an integer type.*/
    virtual TypeRepr* 
    convert_integer_type(Context *context, const llvm::Type* llvm_type);

    /* Convert a floating-point type.*/
    virtual TypeRepr* 
    convert_float_type(Context *context, const llvm::Type* llvm_type);

    /* Convert a function type. */
    virtual TypeRepr*
    convert_function_type(Context *context, const llvm::Type *llvm_type);

    /* Convert a struct type. */
    virtual TypeRepr*
    convert_struct_type(Context *context, const llvm::Type *llvm_type);

    /* Convert an array type. */
    virtual TypeRepr*
    convert_array_type(Context *context, const llvm::Type *llvm_type);

    /* Convert apointer type. */
    virtual TypeRepr*
    convert_pointer_type(Context *context, const llvm::Type *llvm_type);

    /* Convert a vector type. GCC support for vectors (of type
       VECTOR_TYPE) causes some problems so LLVM vectors are converted
       to single dimensioned GCC arrays. */
    virtual TypeRepr* 
    convert_vector_type(Context *context, const llvm::Type* llvm_type);

    /* Convert an opaque type. By default, this is translated to
       (void*). */
    virtual TypeRepr* 
    convert_opaque_type(Context *context, const llvm::Type* llvm_type);

    /* Convert the void type. */
    virtual TypeRepr* 
    convert_void_type(Context *context, const llvm::Type* llvm_type);

    /* Convert a metadata type. By default, this is translated to
       (void*). */
    virtual TypeRepr* 
    convert_metadata_type(Context *context, const llvm::Type* llvm_type);

    /*
     * Assembler strings.
     */

    /* Form a GCC assembler node from LLVM information. */ 
    virtual tree
    make_asm_tree(Context *context,
                  tree type_tree,
                  const std::string& llvm_asm_str,
                  const llvm::InlineAsm::ConstraintInfoVector* llvm_cnstr_vec,
                  const ValueReprSeq* arg_repr_seq,
                  tree output_var_tree);

    /* 
     * Values and expressions
     */

    /* Convert an LLVM location. */
    virtual location_t
    convert_location(Context *context,
                     const llvm::DebugLoc *llvm_location);



    /* Convert an LLVM value. This is the top level function for all
       value conversions, including blocks, instructions and
       expressions. It adds newly created representation objects to
       the tables in context->Scope. */
    virtual ValueRepr*
    convert_value(Context *context, 
                  const llvm::Value *llvm_value);

    /* Convert an integer constant. */
    virtual ValueRepr* 
    convert_constant_int(Context *context,
                         const llvm::Value *llvm_value);

    /* Convert an FP constant (float or double). */
    virtual ValueRepr*
    convert_constant_float(Context *context,
                           const llvm::Value *llvm_value);

    /* Converting constant zero for an aggregate type. */
    virtual ValueRepr*
    convert_constant_zero(Context *context,
                          const llvm::Value *llvm_expr);

    /* Convert a constant string. */
    virtual ValueRepr*
    convert_constant_string(Context *context,
                            const llvm::Value *llvm_value);

    /* Convert a constant array. */
    virtual ValueRepr*
    convert_constant_array(Context *context,
                           const llvm::Value *llvm_value);

    /* Convert a constant struct. */
    virtual ValueRepr*
    convert_constant_struct(Context *context,
                            const llvm::Value *llvm_value);

    /* Convert a constant vector. */
    virtual ValueRepr*
    convert_constant_vector(Context *context,
                            const llvm::Value *llvm_value);

    /* Convert a constant null pointer. */
    virtual ValueRepr*
    convert_constant_pointernull(Context *context,
                                 const llvm::Value *llvm_value);

    /* Converting a constant block-address. */
    virtual ValueRepr*
    convert_constant_blockaddress(Context *context,
                                  const llvm::Value *llvm_value);

    /* Converting a constant expression. */
    virtual ValueRepr*
    convert_constant_expr(Context *context,
                          const llvm::Value *llvm_value);

    /* Convert an undefined value. */
    virtual ValueRepr*
    convert_constant_undef(Context *context,
                           const llvm::Value *llvm_value);

    /*
     * Global Variables
     */

    /* Apply modifiers for a global variable. This handles attributes,
       linkage and similar properties of a variable. */
    virtual VarRepr*
    apply_variable_modifiers(Context *context,
                             const llvm::GlobalVariable* llvm_var,
                             VarRepr* var_repr);

    /* Convert a global variable. */
    virtual VarRepr*
    convert_variable(Context *context, const llvm::Value* llvm_value);

    /* 
     * Instructions (in convert_instrs.cc, except for those related to
     * function handling).
     */

    /* Toplevel for converting an LLVM instruction. This dispatches to
       the correct instruction conversion based on the instruction
       opcode. It does not store the result of the conversion. It is
       used by convert_value to convert instructions.
    */
    virtual InstrRepr* 
    convert_instruction(Context *context,
                        const llvm::Value* llvm_value);

      
    /* Convert a, possibly conditional, branch instruction. */
    virtual InstrRepr* 
    convert_instr_branch(Context *context,
                         const llvm::Instruction *llvm_instr);

    /* Convert an indirect branch instruction. */
    virtual InstrRepr*
    convert_instr_indirectbr(Context *context,
                             const llvm::Instruction *llvm_instr);

    /* Convert a switch instruction. */
    virtual InstrRepr*
    convert_instr_switch(Context *context,
                         const llvm::Instruction *llvm_instr);

    /* Convert an unwind instruction. */
    virtual InstrRepr*
    convert_instr_unwind(Context *context,
                         const llvm::Instruction *llvm_instr);

    /* Convert an unreachable instruction. */
    virtual InstrRepr*
    convert_instr_unreachable(Context *context,
                              const llvm::Instruction *llvm_instr);

    /* Convert a binary operation instruction */
    virtual InstrRepr*
    convert_instr_binop_arith(Context *context,
                              const llvm::Instruction *llvm_instr);

    /* Convert a cast instruction */
    virtual InstrRepr* 
    convert_instr_cast(Context *context,
                       const llvm::Instruction *llvm_instr);

    /* Convert an alloca instruction */
    virtual InstrRepr* 
    convert_instr_alloca(Context *context,
                         const llvm::Instruction *llvm_instr);

    /* Convert a store instruction */
    virtual InstrRepr*
    convert_instr_store(Context *context,
                        const llvm::Instruction *llvm_instr);

    /* Convert a load instruction */
    virtual InstrRepr*
    convert_instr_load(Context *context,
                       const llvm::Instruction *llvm_instr);


    /* Type field indices, used in getelementptr and
       insertvalue/extractvalue instructions, are either integers or
       LLVM expressions. TypeFieldIndex is used so that
       convert_field_expr can be used for both types.
    */
    struct TypeFieldIndex
    {
      // Whether the index is an integer or an LLVM expression.
      bool is_integer;

      // The index.
      union
      {
        size_t value;
        const llvm::Value* llvm_value;
      } index;

      // Constructors.
      explicit TypeFieldIndex()
        : is_integer(false)
      { 
        index.value = 0;
      }

      explicit TypeFieldIndex(size_t idx)
        : is_integer(true)
      { 
        index.value = idx;
      }

      explicit TypeFieldIndex(const llvm::Value* idx)
        : is_integer(false)
      { 
        index.llvm_value = idx;
      }
    };
    typedef std::vector<TypeFieldIndex> TypeFieldIndexSeq;

    /* Convert a type field reference. This is used to handle
       getelementptr and insertvalue/extractvalue instructions.
       Parameter artificial_operands specifies whether the operand
       iterators are artificial (i.e. created by convert_field_index).
    */
    virtual tree 
    convert_field_expr(Context *context,
                       const llvm::Value* llvm_value,
                       TypeFieldIndexSeq* index_seq);

    /* Convert a type field index. This is used to handle extractvalue and
       insertvalue instructions.*/
    virtual tree 
    convert_field_index(Context *context,
                        const llvm::Value* llvm_value,
                        const unsigned num_indices,
                        const unsigned *index_begin,
                        const unsigned *index_end);

    /* Convert an extractelement instruction. */
    virtual InstrRepr* 
    convert_instr_extractelement(Context *context,
                                 const llvm::Instruction *llvm_instr);
      
    /* Convert an insertelement instruction. */
    virtual InstrRepr* 
    convert_instr_insertelement(Context *context,
                                const llvm::Instruction *llvm_instr);


    /* Convert a shufflevector instruction. */
    virtual InstrRepr*
    convert_instr_shufflevector(Context *context,
                                const llvm::Instruction *llvm_instr);

    /* Convert an extractvalue instruction. */
    virtual InstrRepr* 
    convert_instr_extractvalue(Context *context,
                               const llvm::Instruction *llvm_instr);

    /* Convert an insertvalue instruction. */
    virtual InstrRepr*
    convert_instr_insertvalue(Context *context,
                              const llvm::Instruction *llvm_instr);

    /* Convert a getelementptr instruction. */
    virtual InstrRepr*
    convert_instr_getelementptr(Context *context,
                                const llvm::Instruction *llvm_instr);

    /* Convert an LLVM predicate for a give type. */
    virtual tree_code
    convert_cmp_predicate(Context *context,
                          const llvm::CmpInst::Predicate llvm_pred,
                          const llvm::Type* llvm_type);

    /* Convert an cmp instruction. This is the worker function for
       convert_instr_icmp and convert_instr_fcmp.  */
    virtual InstrRepr* 
    convert_instr_cmp(Context *context,
                      const llvm::Instruction* llvm_instr);

    /* Convert a PHI instruction */
    virtual InstrRepr*
    convert_instr_phi(Context *context,
                      const llvm::Instruction *llvm_instr);

    /* Convert a select instruction. */
    virtual InstrRepr* 
    convert_instr_select(Context *context,
                         const llvm::Instruction* llvm_instr);


    /* Convert a vaarg instruction. */
    virtual InstrRepr* 
    convert_instr_vaarg(Context *context,
                        const llvm::Instruction* llvm_instr);

    /*
     * Functions and function calls (in convert_functions.cc).
     */

    /* Convert a return instruction. */
    virtual InstrRepr* 
    convert_instr_ret(Context *context,
                      const llvm::Instruction *llvm_instr);

    /* Convert an LLVM value identifying a function. */
    virtual ValueRepr* 
    convert_function_value(Context *context, 
                           const llvm::Value *llvm_fnval);


    /* Convert an LLVM parameter. May be called as part of building a
       function declaration in which case the function context and
       representation may not be available (context->fn_context == 0
       or context->fn_context->fn_repr == 0).
    */
    virtual ValueRepr*
    convert_parameter(Context *context, const llvm::Value* llvm_value);

    /* Convert an inline asm instruction. */
    virtual InstrRepr*
    convert_instr_asm(Context *context,
                      const llvm::Instruction* llvm_instr);

    /* Build a function call from a converted function value and
       unconverted operands. */
    virtual tree
    build_function_call(Context *context,
                        location_t loc,
                        TypeRepr* type_repr,
                        ValueRepr* fnptr_repr,
                        ValueReprSeq* arg_repr_seq,
                        const bool is_variadic);

    /* Convert a call to an LLVM intrinsic function. Return 0 if the
       function call is not handled as a special case. */
    virtual tree
    convert_call_intrinsic(Context *context,
                           const llvm::CallInst* llvm_call,
                           const llvm::Type* llvm_type,
                           const llvm::Value* llvm_fnptr);

    /* Convert a call to a GCC built-in function. Return 0 if the
       function call is not handled as a special case. */
    virtual tree
    convert_call_builtin(Context *context,
                         const llvm::Instruction* llvm_instr,
                         const llvm::Type* llvm_type,
                         const llvm::Value* llvm_fnptr);

    /* Construct function calls for convert_instr_call and
       convert_instr_invoke. */
    virtual tree
    convert_function_call(Context *context,
                          const llvm::Instruction* llvm_instr,
                          const llvm::Value* llvm_fnptr,
                          const unsigned num_args);

    /* Convert a call instruction. */
    virtual InstrRepr*
    convert_instr_call(Context *context,
                       const llvm::Instruction* llvm_instr);

    /* Convert an invoke instruction. */
    virtual InstrRepr*
    convert_instr_invoke(Context *context,
                         const llvm::Instruction* llvm_instr);

    /* Apply modifiers to a function. This handles attributes, linkage
       and similar properties. */
    virtual FunctionRepr*
    apply_function_modifiers(Context *context,
                             const llvm::Function* llvm_function,
                             FunctionRepr* fn_repr);

    /* Make a declaration for an LLVM intrinsic function. */
    virtual FunctionRepr* 
    make_intrinsic_decln(Context *context, 
                         const llvm::Function *llvm_function);

    /* Make a declaration for an LLVM function. */
    virtual FunctionRepr* 
    make_function_decln(Context *context, 
                        const llvm::Function *llvm_function);

    /* Convert an LLVM function declaration. This is used to declare
       functions before converting the function definitions. */
    virtual FunctionRepr* 
    convert_function_decln(Context *context, 
                           const llvm::Value* llvm_value);

    /* Convert an LLVM function, adding the new declaration to the
       global context.. */
    virtual FunctionRepr* 
    convert_function(Context *context, 
                     const llvm::Value* llvm_value);

    /*
     * Basic blocks (in convert_functions.cc).
     */

    /* Create and set-up the representation for a basic block. This
       generates the representation and the label declaration for the
       block.
    */
    virtual BlockRepr*
    setup_block_repr(Context *context, 
                     const llvm::BasicBlock *llvm_block,
                     const bool is_entry);

    /* Form the final list of instructions for a basic block, after all
       code has been generated.  This handles any extra code that must
       be added to the code generated for the basic block's
       instructions.  */
    virtual BlockRepr* 
    finalize_block_repr(Context* context,
                        BlockRepr* block_repr);


    /* Convert an LLVM basic block, generating a list of instructions. This is
       the top-level function for converting basic blocks.
    */
    virtual BlockRepr* 
    convert_basic_block(Context *context, const llvm::Value *llvm_block);
  };

} // end of namespace llvmir

/* End of file */
#endif /* PARSER_H */
